#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <getopt.h>
#include "list.h"

#define MEDIA 0
#define DESVIACION_ESTANDAR 1
#define MEDIANA 2

#define NUM_CONVERT_FUNCTION atof

typedef int bool;
#define false (0)
#define true (1)

NUMTYPE media(NUMTYPE sumatoria, int cantidad) {
	return sumatoria / cantidad;
}

NUMTYPE desviacion_estandar(NUMTYPE sumatoria_de_cuadrados, NUMTYPE sumatoria, int cantidad) {
	// la media del cuadrado de la variable menos el cuadrado de la media
	NUMTYPE med = media(sumatoria,cantidad);
	NUMTYPE varianza = sumatoria_de_cuadrados / cantidad - med*med;
	return sqrt(varianza);
}



int letter_to_function( char character) {
	if (character == 'e') return MEDIA;
	if (character == 'm') return MEDIANA;
	if (character == 's') return DESVIACION_ESTANDAR;
	
	return -1;
}

void help() {
printf(
"Usage:\n"
"	tp0 -h				Display this message and exit.\n"
"	tp0 -V				Display version.\n"
"	tp0 -i --infile			Input file. - stands for stdin.\n"
"	tp0 -o --outfile		Output file. - stands for stdout.\n"
"	tp0 -c --command		One of (e,m,s).\n"
);
	
	
}

void version() {
printf("TP0 v1.0\n");
	
}

int main(int argc, char* argv[] )  {
	
	const char* input_file_path = "/dev/stdin";
	const char* output_file_path = "/dev/stdout";
	char* next_token;
	char* pch;
	char* last_token;
	int acumulado = false;
	
	int buffer_position = 0;
	char buffer_acumulado[64];

	int len;

	int function = -1;
	
	// calculo de media
	NUMTYPE sumatoria = 0;

	// calculo de desviacion estandar
	NUMTYPE sumatoria_de_cuadrados = 0;
	
	// comun
	int cantidad = 0;
	
        struct option long_options[] = {
            {"infile", 1, 0, 0},
            {"outfile", 1, 0, 0},
            {"command", 1, 0, 0},
            {"help", 0, 0, 0},
            {0, 0, 0, 0}
         };
	 
	 while (1) {
		 int option_index = 0;
		 int c;
		 
		 c = getopt_long(argc, argv, "i:o:c:hV", long_options, &option_index);
		 if (c == -1) break;
		 
		 switch (c) {
			case 0:
				switch(option_index) {
					case 0:
						input_file_path = optarg;
						break;
					case 1:
						output_file_path = optarg;
						break;
					case 2:
						function = letter_to_function( optarg[0] );
						if (function == -1) {
							fprintf(stderr, "Invalid function %s\n", optarg );
							help();
							return 1;
						}
						break;
					case 3:
						help();
						return 0;
				}
				break;
			case 'i':
				input_file_path = optarg;
				break;
			case 'o':
				output_file_path = optarg;
				break;
			case 'c':
				function = letter_to_function( optarg[0] );
				if (function == -1) {
					fprintf(stderr, "Invalid function %s\n", optarg );
					help();
					return 1;
				}
				break;
			case 'h':
				help();
				return 0;
			case 'V':
				version();
				return 0;
		 }
	 }
	
	if (function == -1) {
		fprintf(stderr, "Function not specified\n", optarg );
		help();
		return 1;
	}
	FILE* input_file;
	FILE* output_file;
	
	output_file = fopen(output_file_path, "w");
	 
	if (!output_file) {
		fprintf(stderr,"Cannot open %s for write\n", output_file_path);
		return 1;
	}
	 
	input_file = fopen(input_file_path, "r");
	 
	if (!input_file) {
		fprintf(stderr,"Cannot open %s for read\n", input_file_path);
		return 1;
	}
	
	List* list = NULL;
	if (function == MEDIANA) {
		list = createList();
	}
	
	while (!feof(input_file)) {

		int caracter;
		
		caracter = fgetc(input_file);
		
		if (caracter == '\n' || caracter == ' ' || caracter == -1) {
			
			if (buffer_position > 0) {
				
				NUMTYPE num;
				
				buffer_acumulado[buffer_position] = '\0';

				num = NUM_CONVERT_FUNCTION(buffer_acumulado);
				cantidad++;
				switch (function) {
					case DESVIACION_ESTANDAR:
						sumatoria_de_cuadrados += num*num;
					case MEDIA:
						sumatoria += num;
						break;
					case MEDIANA:
						// inserta en la lista de manera ordenada
						insertToList(list,num);
						break;
				};
				
				buffer_position = 0;
			}
		} else {
			buffer_acumulado[buffer_position] = caracter;
			buffer_position++;
		}
		
		if (caracter == -1) {
			break;
		}
	}

			
	switch (function) {
		case MEDIA:
			fprintf(output_file, "%f\n", media(sumatoria,cantidad) );
			break;
		case DESVIACION_ESTANDAR:
			fprintf(output_file, "%f\n", desviacion_estandar(sumatoria_de_cuadrados,sumatoria,cantidad));
			break;
		case MEDIANA: {
			
			int listSize = nodeCount(list);
		
			NUMTYPE mediana;
		
			if (listSize % 2 == 0) {

				ListNode* node;
				ListNode* node2;
				// toma el promedio de los dos valores del "medio" listSize/2-1 y el siguiente
				node = seekNode(list, listSize/2-1);
				node2 = node->next;
				
				mediana = (node->value + node2->value)/2;
			} else {
				// toma el valor del "medio" (listSize-1)/2
				ListNode* node;
				
				node = seekNode(list, (listSize-1)/2);
				mediana = node->value;
				
			}
		
			fprintf(output_file, "%f\n", mediana);
			destroyList(list);
			break;
		}
	}
	
	fclose(output_file);
	fclose(input_file);
	return 0;
}

